package com.atguigu.crowd.mvc.config;

import com.atguigu.crowd.constant.CrowdConstant;
import com.atguigu.crowd.exception.LoginAcctAlreadyInUseException;
import com.atguigu.crowd.exception.LoginAcctAlreadyInUseForUpdateException;
import com.atguigu.crowd.exception.LoginFailedException;
import com.atguigu.crowd.util.CrowdUtil;
import com.atguigu.crowd.util.ResultEntity;
import com.google.gson.Gson;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

// @ControllerAdvice表示当前类是一个基于注解方式的异常处理器类,当handler层有异常发生的时候，这个类会自动对异常进行处理
@ControllerAdvice
public class CrowdExceptionResolver {

    //处理修改失败异常
    @ExceptionHandler(value = LoginAcctAlreadyInUseForUpdateException.class)
    public static ModelAndView resolverLoginAcctAlreadyInUseForUpdateException(
            LoginAcctAlreadyInUseForUpdateException exception,
            HttpServletRequest request,
            HttpServletResponse response
    ) throws IOException {
        String viewName = "system-error"; //不回到更新的表单页面是因为没有携带能够回显表单的数据
        return commonResolveException(exception, request, response, viewName);
    }

    //处理登陆失败异常
    @ExceptionHandler(value = Exception.class)
    public static ModelAndView resolveException(
            Exception exception,
            HttpServletRequest request,
            HttpServletResponse response
    ) throws IOException {
        String viewName = "system-error";
        return commonResolveException(exception, request, response, viewName);
    }

    //处理登陆失败异常
    @ExceptionHandler(value = LoginFailedException.class)
    public static ModelAndView resolverLoginFailedException(
            LoginFailedException exception,
            HttpServletRequest request,
            HttpServletResponse response
    ) throws IOException {
        String viewName = "admin-login";
        return commonResolveException(exception, request, response, viewName);
    }

    //@ExceptionHandler将一个具体的异常类型和一个方法关联起来
    @ExceptionHandler(value = NullPointerException.class)
    public static ModelAndView resolverNullPointerException(
            NullPointerException exception,
            HttpServletRequest request,
            HttpServletResponse response
            ) throws IOException {
        String viewName = "system-error";
        return commonResolveException(exception, request, response, viewName);
    }

    /**
     * 核心异常处理方法
     * @param exception SpringMVC 捕获到的异常对象
     * @param request 为了判断当前请求是“普通请求”还是“Ajax 请求” 需要传入原生 request 对象
     * @param response 为了能够将 JSON 字符串作为当前请求的响应数据返回给浏览器
     * @param viewName 指定要前往的视图名称
     * @return ModelAndView
     * @throws IOException
     */
    /*
     * 单独写这个方法的目的是因为：当我们要处理很多不同种类的异常时，都需要这段代码，为了防止代码冗余，所以把它抽取出来写。
     */
    private static ModelAndView commonResolveException(
            Exception exception,
            HttpServletRequest request,
            HttpServletResponse response,
            String viewName
    ) throws IOException {

        // 1.判断当前请求是“普通请求”还是“Ajax 请求”
        boolean judgeResult = CrowdUtil.judgeRequestType(request);

        // 2.如果是 Ajax 请求
        if(judgeResult) {

            // 3.从当前异常对象中获取异常信息
            String message = exception.getMessage();

            // 4.创建 ResultEntity
            ResultEntity<Object> resultEntity = ResultEntity.failed(message);

            // 5.创建 Gson 对象
            Gson gson = new Gson();

            // 6.将 resultEntity 转化为 JSON 字符串
            String json = gson.toJson(resultEntity);

            // 7.把当前 JSON 字符串作为当前请求的响应体数据返回给浏览器 jquery会按照json格式进行解析。
            // ①获取 Writer 对象
            PrintWriter writer = response.getWriter();
            // ②写入数据
            writer.write(json);
            // 8.返回 null，不给 SpringMVC 提供 ModelAndView 对象
            // 这样 SpringMVC 就知道不需要框架解析视图来提供响应，而是程序员自己提供了响应
            return null;
        }
        // 9.如果不是Ajax请求，则说明是普通请求，则创建 ModelAndView 对象
        ModelAndView modelAndView = new ModelAndView();

        // 10.将 Exception 对象存入模型
        modelAndView.addObject(CrowdConstant.ATTR_NAME_EXCEPTION, exception);

        // 11.设置目标视图名称
        modelAndView.setViewName(viewName);

        // 12.返回 ModelAndView 对象
        return modelAndView;
    }
 } //类结束









